// pai21.c 21点牌
// make by 猫部猫(Catyboy)

#include <ansi.h>
#define MAX_PAI  52

inherit ITEM;

int has_start;
mapping player;            // 玩家ID
mapping player_data;

// 参数
int game_start;            // 1局是否开始?
string who_play;        // 正在出牌的人

// 牌容器
int remain;                // 所剩的牌数量
mixed pai_on_desk;        // 可以使用的牌
mapping player_hand;    // 玩家手上的牌

// 计数器
mapping counter = ([]);

string *suit_str = ({  WHT"方块"NOR , HIB"梅花"NOR ,HIR"红桃"NOR , HBWHT BLK"黑桃"NOR});
string *suit_char = ({ "D","C","H","S"});
string *rank_str = ({ "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A" });

int pai_number(int pai);
int pai_color(int pai);
object get_player(string id);
int is_playing(object ob);
int sizeof_pai(mixed* p);
void restore_player_data();
int pick_out();
int pick_in(string to, int card);
string id_to_pai(int id);
int do_pass(string arg);
int do_nextone(string arg);
string view_pai(string which);
void finish_21();
int auto_order();

void msg(object me,object who,string msg)
{
    if(me)
        message_vision(msg,me,who);
    else
        tell_room(environment(this_object()),msg,0);
}

int c21(mixed* inv)
{
    int i,j,sum,temp;

    sum = 0;
    temp = 0;
    for(i=0;i<inv[0];i++)
    {
        j = pai_number(inv[i+1]);
        if(j == 14)
        {
            temp ++;
            j = 11;
        }
        else
            j = (j>10?10:j);
        sum+=j;
    }

    for(;sum>21&&temp>0;temp--)
        sum -= 10;

    return sum;
}

void reset_pai()
{
    // 重置牌（牌盒，玩家的牌)
    int i;
    string* key;

    for(i=0;i<MAX_PAI;i++)
        pai_on_desk[i]=i;
    remain=MAX_PAI;

    key = keys(player);
    for(i=0;i<sizeof(key);i++)
        player_hand[key[i]]=allocate(MAX_PAI);

    who_play = 0;
}

int reset_all()
{
    // 重置整个游戏
//    int i;

    has_start = 0;
    if(sizeof(player)!=0)
        msg(this_player(),0,"$N重新置牌了。\n");

    player = ([]);
    player_hand = ([]);

    pai_on_desk = allocate(MAX_PAI);
    reset_pai();
    counter = ([]);

    return 1;
}

void create()
{
    set_name("21点牌桌", ({"card desk","pai","pai21"}));
    if( clonep() )
        set_default_object(__FILE__);
    else {
        set("long", "一张桌子，上面放着一些牌，使用方法请使用(helppai)命令。\n");
        set("unit", "张");
        set("value", 1);
    }
    player = ([]);
    reset_all();
}

void init()
{
    // 帮助
    add_action("do_help", "helppai");

    // 基本指令
    add_action("do_reset", "reset");        // 重置整个游戏
    add_action("do_restart", "restart");    // 重新游戏
    add_action("do_join", "join");            // 加入牌局
    add_action("do_start", "start");        // 开始游戏

    add_action("do_xipai", "xipai");        // 洗牌
    add_action("do_view", "view");            // 看牌

    add_action("do_nextone", "next");        // 催促下一个
    add_action("do_pass", "pass");            // 不要牌

    // 扩展指令
    add_action("do_chupai", "play");        // 玩
    add_action("do_daopai", "daopai");        // 倒牌

    // 计数器
    add_action("do_showc", "showc");        // 显示计数器
}

int do_reset(string arg)
{
    if(arg == "counter")
    {
        if(!is_playing(this_player()))
            return notify_fail("你都不玩牌啊！\n");

        msg(this_player(),0,"$N复位计数器了。\n");
        counter = ([]);
        return 1;
    }
    if(!this_object()->id(arg))
        return 0;

    return reset_all();
}

int do_restart(string arg)
{
//    int i;
//    string* key;

    if(!is_playing(this_player()))
        return notify_fail("你都不玩，开始什么啊！\n");

    if(!has_start)
        return notify_fail("游戏还没有开始。\n");

    if(sizeof(player)!=0)
        msg(this_player(),0,"$N开始新的一轮牌局了。\n\n");

    reset_pai();
    restore_player_data();
    msg(0,0,"在开始游戏之前(play)，请各位洗牌(xipai)。\n");
    return 1;
}

int do_join(string arg)
{
    object me;

    me = this_player();
    if(has_start)
        return notify_fail("牌局已经开始，不能加入了。\n");
    if(is_playing(me))
        return notify_fail("你已经参加了。\n");

    // add player
    player[me->query("id")]="yes";
    player_hand[me->query("id")]=allocate(MAX_PAI);
    msg(me,0,"$N加入牌局了。\n");
    return 1;
}

int do_start(string arg)
{
    if(!is_playing(this_player()))
        return notify_fail("你都不玩，开始什么啊！\n");

    if(has_start)
        return notify_fail("牌局已经开始了。\n");

    has_start = 1;
    msg(this_player(),0,"$N开始了牌局。\n\n");
    auto_order();
    msg(0,0,"在开始游戏之前(play)，请各位洗牌(xipai)。\n");
    return 1;
}

int do_xipai(string arg)
{
    int i,sum,rand,which,temp;
    mixed* pai;

    if(!is_playing(this_player()))
        return notify_fail("你都不玩，洗什么牌啊！\n");

    pai = pai_on_desk;
    sum = remain;
    rand = sum;

    for(i=0;i<sum;i++)
    {
        which = random(rand);
        temp = pai[rand-1];
        pai[rand-1] = pai[which];
        pai[which] = temp;
        rand --;
    }
    msg(this_player(),0,"$N洗了洗桌面的牌。\n");
    return 1;
}

int do_chupai(string arg)
{
    int /*i,*/sum/*,j*/,done/*,temp*/,id;
    mixed* inv;
//    string cmd;
    object ob;

    if(!is_playing(this_player()))
        return notify_fail("你都不玩啊！\n");

    if(who_play==0)
        who_play = this_player()->query("id");

    if(who_play!=this_player()->query("id"))
        return notify_fail("还没有轮到到你啊。\n");

    done = 0;

    if((id = pick_out())==-1)
        return 0;

    if(!pick_in(this_player()->query("id"),id))
        return 0;

    msg(this_player(),0,"$N摸了一张"+id_to_pai(id)+"。\n");

    done = 1;

    // check is over 21,if over pass
    inv = player_hand[this_player()->query("id")];
    sum = c21(inv);

    msg(this_player(),0,"$N手上有"HIY+sum+"点"NOR"了。\n");
    if(sum>21)
    {
        do_pass(HIR"爆了！！！"NOR"\n");
        return 1;
    }

    ob = get_player(player[who_play]);
    if(ob)
    {
        who_play = player[who_play];
        do_nextone("");
    }
    return 1;
}

int do_view(string arg)
{
    mixed* pai;
    string hand_name;

    if(arg==""||arg==0)
        arg = this_player()->query("id");
    else
        hand_name = arg;

    pai = player_hand[arg];
    if(pai==0)
        return notify_fail("牌局没有这个玩家。\n");

    if(hand_name)
        write(hand_name+"：");
    write(view_pai(arg)+"\n");
    return 1;
}

string extra_long()
{
    object me,who;
    string r,key;
    mixed *idx;
    int i;

    me = this_player();
    idx = keys(player);

    r = sprintf("牌盒里面有%d张牌\n",remain);

    if(sizeof(player)>0)
    {
        r+="******************************************\n";
        for(i=0;i<sizeof(idx);i++)
        {
            key = idx[i];
            who = get_player(key);
            if(who)
                r = r + key + "："+view_pai(key)+"\n";
        }
        r = r + "******************************************\n";
    }
    return r;
}

int pick_out()
{
    int s,id;

    if(remain>0)
    {
        remain--;
        s = remain;
        id = pai_on_desk[s];
        pai_on_desk[s]=-1;
        return id;
    }
    else
    {
        msg(0,0,"没有牌了");
        return -1;
    }
}

int pick_in(string to, int card)
{
    int s;
    mixed* pai;

    pai = player_hand[to];
    s = pai[0];
    s++;
    pai[s]=card;
    pai[0]++;
    return 1;
}

object get_player(string id)
{
    if(stringp(player[id]))
        return find_player(id);
    return 0;
}

int is_playing(object ob)
{
//    int i;
    string id;
    id = ob->query("id");
    if(stringp(player[id]))
        return 1;
    return 0;
}

int sizeof_pai(mixed* p)
{
    return p==0?0:p[0];
}

string view_pai(string which)
{
    mixed* pai;
    int /*show_all,*/i/*,j*/;
    string s,p;

    pai = player_hand[which];

    if(pai==0)
        return 0;

    if(sizeof_pai(pai)==0)
        return "没有牌";

    s = "";
    for(i=0;i<pai[0];i++)
    {
        p = id_to_pai(pai[i+1]);
        s+= p +" ";
    }
    return sprintf("%s：%d点",s,c21(pai));
}

int do_nextone(string arg)
{
    object ob;
    string id;

    if(!is_playing(this_player()))
        return notify_fail("你都不玩牌啊！\n");

    if(who_play==0)
        return notify_fail("现在是谁出牌啊？\n");

    id = who_play;
    ob = get_player(id);

    if(ob==0)
        return msg(0,0,"有玩家缺场了，请重新开始游戏(reset pai)。\n");

    if(this_player()->query("id")!=id)
        msg(this_player(),ob,"$N对$n说道：到你了。\n");
    else
        msg(this_player(),0,"对自己说道：到我啦！\n");
    return 1;
}

int do_pass(string arg)
{
    string old/*,str*/;
    string* key;
//    mixed* inv;
    int i/*,temp*/;

    if(!is_playing(this_player()))
        return notify_fail("你都不玩牌啊！\n");

    if(who_play!=this_player()->query("id"))
        return notify_fail("还没有到你啊！\n");

    if(!arg)
        arg = "ＰＡＳＳ！！";
    msg(this_player(),0,"$N说道："+arg+"\n");
    old = who_play;
    who_play = player[who_play];

    key = keys(player);

    for(i=0;i<sizeof(key);i++)
        if(player[key[i]]==old)
            player[key[i]] = player[old];
    player[old] = "";

    if(player[who_play] == "")
    {
        msg(0,0,"大家都不要了。\n");
        finish_21();
        return 1;
    }
    do_nextone("");
    return 1;
}

string id_to_pai(int id)
{
    int i,j;
    if(id <0)
        return HIR"???"NOR;
    if(id == 52)
        return HIY"小鬼"NOR + "JS";
    if(id>=53)
        return HIY"大鬼"NOR + "JB";
    i = id/13;
    j = id-i*13;
    return suit_str[i] + suit_char[i] + rank_str[j];
}

int pai_to_id(string id)
{
    int ic1=-1,ic2=-1;
    string c1,c2;
    c1 = id[0..0];
    c2 = id[1..sizeof(id)-1];

    if(c1=="J")return c2=="S"?52:53;
    if(c1==suit_char[0]) ic1=0;
    if(c1==suit_char[1]) ic1=1;
    if(c1==suit_char[2]) ic1=2;
    if(c1==suit_char[3]) ic1=3;
    if(ic1==-1)return -1;

    if(sscanf(c2,"%d",ic2)!=1)
    {
        if(c2 == "J") ic2 = 11;
        if(c2 == "Q") ic2 = 12;
        if(c2 == "K") ic2 = 13;
        if(c2 == "A") ic2 = 14;
        if(ic2==-1) return -2;
    }
    ic2 -=2;
    return ic1*13+ic2;
}

int auto_order()
{
    int i;
    string* key;
    string who,start_one;

    key = keys(player);

    start_one = key[0];
    who = "";
    for(i=0;i<sizeof(key);i++)
    {
        if(get_player(key[i]))
        {
            if(who =="")
                who = key[i];
            else
            {
                player[who] = key[i];
                who = key[i];
            }
        }
    }
    player[who] = start_one;
    return 1;
}

int do_showc(string arg)
{
    string r;
    mixed *idx;
    int i;

    idx = keys(counter);

    r = "计数表(Scoreboard)\n－－－－－－－－－－－－－－－－－\nID　　　　　　　　分数\n－－－－－－－－－－－－－－－－－\n";
    for(i=0;i<sizeof(idx);i++)
        r = sprintf("%s%-14s%10d\n", r,idx[i],counter[idx[i]]);
    r += "－－－－－－－－－－－－－－－－－\n";

    msg(0,0,r);
    return 1;
}

void press_counter(string id,int num)
{
    counter[id]=counter[id]+num;
}

int do_daopai(string arg)
{
    int i,id;

    if(!is_playing(this_player()))
        return notify_fail("你都不玩，搞什么啊！\n");

    if(!has_start)
        return notify_fail("游戏还没有开始。\n");

    if(remain>0)
    {
        id = pai_on_desk[0];
        for(i=1;i<remain;i++)
            pai_on_desk[i-1] = pai_on_desk[i];
        pai_on_desk[remain-1] = id;
        msg(this_player(),0,"$N倒了一下牌。\n");
        return 1;
    }
    else
        return notify_fail("没有牌了。\n");
}

int pai_number(int pai)
{
    if(pai==52)
        return 15;
    if(pai==53)
        return 16;
    return pai-pai/13*13+2;
}

int pai_color(int pai)
{
    if(pai>=52)
        return 5;
    return pai/13+1;
}

void save_player_data()
{
    string* inv;
    int i;
    inv = keys(player);
    player_data = ([]);
    for(i=0;i<sizeof(inv);i++)
        player_data[inv[i]] = player[inv[i]];
}

void restore_player_data()
{
    int i;
    string* key;

    if(!player_data)
        return;

    key = keys(player_data);
    for(i=0;i<sizeof(key);i++)
        player[key[i]] = player_data[key[i]];
}

int sort_21(string str1, string str2)
{
    int c1,c2;
    string temp;

    sscanf(str1,"%s:%d",temp,c1);
    sscanf(str2,"%s:%d",temp,c2);

    if(c1==c2)
        return 0;

    if(c1>21&&c2>21)
        return (c1<c2)?-1:1;

    if(c1>21)
        c1 = -1;
    if(c2>21)
        c2 = -1;
    return (c1>c2)?-1:1;
}

void finish_21()
{
    string* key;
    mixed* inv2;
    mixed* inv;
    string str,r,id;
    int i,maxn,fen,k,lpoint;

    key = keys(player);
    inv2 = allocate(sizeof(player));
    for(i=0;i<sizeof(key);i++)
    {
        str = key[i];
        inv2[i] = str + ":" + c21(player_hand[str]);
    }

    inv = sort_array(inv2,"sort_21",this_object());

    lpoint = -1;
    r = "结果\n－－－－－－－－－－－－－－－－－\nID　　　　　　　　点数       得分\n－－－－－－－－－－－－－－－－－\n";
    for(i=0;i<sizeof(inv);i++)
    {
        sscanf(inv[i],"%s:%d",id,k);
        if(k != lpoint)
        {
            fen = sizeof(inv) - i - 1;
            lpoint = k;
        }
        if(i==0)maxn=k;
        if(k==maxn)r+=HIY;
        if(k>21)
        {
            r+=HIR;
            fen = 0;
        }
        r = sprintf("%s%-14s%5d%12d\n", r,id,k,fen);
        r+=NOR;

        press_counter(id,fen);
    }
    r += "－－－－－－－－－－－－－－－－－\n\n";
    msg(0,0,r);

    reset_pai();
    restore_player_data();
    msg(0,0,"请各位重新洗牌(xipai)开始新的一局。\n");
}

int do_help(string arg)
{
    this_player()->start_more( @HELP
玩21点牌桌使用方法:
----[开始游戏]------------------------------
帮助命令：helppai
加入命令：join　　　　　加入游戏
开始命令：start 　　　　开始以后不能再加入
重置命令：reset pai 　　结束游戏

----[游戏命令]------------------------------
重新游戏：restart
　　摸牌：play　　　　　进行游戏
不再要牌: pass

----[其它命令]------------------------------
洗牌命令：xipai
看牌命令：view [玩家ID] 观察玩家手上的牌
催促命令：next　　　　　催促正在出牌的人

----[计数牌使用]----------------------------
显示: showc
重置: reset counter




----[游戏规则]------------------------------
游戏开始以后，每人轮流摸牌直到不摸为止。
最后根据每人手上的点数决定胜负。
点数 > 21 为输，点数<21的时候点数大者胜。
J，Q，k为10点，A可以为1点或者为11点。

--------------------------------------------
            make by 猫部猫(Catyboy) v1.0
HELP
    );
        return 1;
}

mixed t()
{
    return pai_on_desk;
}
